package com.wcl.hencodertest.test12_scale_imageview;

import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.View;
import android.widget.OverScroller;

import androidx.annotation.Nullable;
import androidx.core.view.GestureDetectorCompat;

import com.blankj.utilcode.util.ConvertUtils;
import com.blankj.utilcode.util.LogUtils;
import com.wcl.hencodertest.Util;

/**
 * @项目名： HenCoderTest
 * @包名： com.wcl.hencodertest
 * @文件名: ScalableImageView
 * @创建者: kilin
 * @创建时间: 2020/5/6 20:23
 * @描述： TODO
 */
public class ScalableImageView extends View {
    private static final String TAG = "ScalableImageView";
    public static final float OVER_SCALE = 1F;

    public static final int BITMAP_WIDTH = ConvertUtils.dp2px(300);
    private final MyOnScaleGestureListener onScaleGestureListener;
    Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private final Bitmap mBitmap;
    private float originalOffsetX;
    private float originalOffsetY;
    private float offsetX;
    private float offsetY;
    private GestureDetectorCompat doubleDetector;
    private MyOnGestureListener onGestureListener;
    private final MyOnDoubleTapListener onDoubleTapListener;
    private float scaleSmall;
    private float scaleBig;
    private int currentScaleIndex = 0;
    private float[] scaleArray;

    private float currentScale = 1;
    private ObjectAnimator animator;
    //    private float fraction;
    private float oldScale = 1;
    private final OverScroller overScroller;
    private float oldOffsetX;
    private float oldOffsetY;
    private final ScaleGestureDetector scaleGestureDetector;

    public ScalableImageView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        mBitmap = Util.getAvatar(BITMAP_WIDTH);
        onGestureListener = new MyOnGestureListener();
        onDoubleTapListener = new MyOnDoubleTapListener();
        onScaleGestureListener = new MyOnScaleGestureListener();

        doubleDetector = new GestureDetectorCompat(context, onGestureListener);
        doubleDetector.setOnDoubleTapListener(onDoubleTapListener);
        overScroller = new OverScroller(context);
        scaleGestureDetector = new ScaleGestureDetector(context, onScaleGestureListener);
    }


    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        originalOffsetX = (getWidth() - mBitmap.getWidth()) / 2F;
        originalOffsetY = (getHeight() - mBitmap.getHeight()) / 2F;
        if ((float) mBitmap.getWidth() / getWidth() > (float) mBitmap.getHeight() / getHeight()) {
            scaleSmall = getWidth() / (float) mBitmap.getWidth();
            scaleBig = getHeight() / (float) mBitmap.getHeight() * OVER_SCALE;
        } else {
            scaleSmall = getHeight() / (float) mBitmap.getHeight();
            scaleBig = getWidth() / (float) mBitmap.getWidth() * OVER_SCALE;
        }
        scaleArray = new float[]{1, scaleBig};
    }


    @Override
    protected void onDraw(Canvas canvas) {
        float fraction = (currentScale - oldScale) / (scaleBig - 1);
        Log.i(TAG, "onDraw: offsetX: " + offsetX * fraction + "    offsetY: " + offsetY * fraction);
        canvas.translate(offsetX * fraction + (1 - fraction) * oldOffsetX, offsetY * fraction + (1 - fraction) * oldOffsetY);

        canvas.scale(currentScale, currentScale, getWidth() / 2F, getHeight() / 2F);
        canvas.drawBitmap(mBitmap, originalOffsetX, originalOffsetY, paint);
    }

    private ObjectAnimator getAnimator() {
        if (animator == null) {
            animator = ObjectAnimator.ofFloat(this, "currentScale", 0, 1);
        }
        animator.setFloatValues(1, scaleBig);
        return animator;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        boolean result = scaleGestureDetector.onTouchEvent(event);
        if (!scaleGestureDetector.isInProgress()) {
            result = doubleDetector.onTouchEvent(event);
        }
        return result;
    }

    class MyOnDoubleTapListener implements GestureDetector.OnDoubleTapListener {

        @Override
        public boolean onSingleTapConfirmed(MotionEvent e) {
            return false;
        }

        @Override
        public boolean onDoubleTap(MotionEvent e) {
            currentScaleIndex = currentScaleIndex % scaleArray.length;
            oldScale = scaleArray[currentScaleIndex];
            currentScaleIndex++;
            currentScaleIndex = currentScaleIndex % scaleArray.length;
            currentScale = scaleArray[currentScaleIndex];
            oldOffsetX = offsetX;
            oldOffsetY = offsetY;
            offsetX = (e.getX() - getWidth() / 2F) - (e.getX() - getWidth() / 2F) * currentScale;
            offsetY = (e.getY() - getHeight() / 2F) - (e.getY() - getHeight() / 2F) * currentScale;
            LogUtils.i("onDoubleTap: ", "offsetX: " + offsetX, "offsetY: " + offsetY);
            if (currentScale > oldScale) {
                getAnimator().start();
            } else {
                getAnimator().reverse();
            }
            ScalableImageView.this.invalidate();
            return true;
        }

        @Override
        public boolean onDoubleTapEvent(MotionEvent e) {
            return false;
        }
    }


    class MyOnGestureListener implements GestureDetector.OnGestureListener, Runnable {

        @Override
        public boolean onDown(MotionEvent e) {
            return true;
        }

        @Override
        public void onShowPress(MotionEvent e) {

        }

        @Override
        public boolean onSingleTapUp(MotionEvent e) {
            return false;
        }

        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            float bitmapShowWidth = mBitmap.getWidth() * currentScale;
            float bitmapShowHeight = mBitmap.getHeight() * currentScale;
            if (bitmapShowWidth > getWidth()) {
                offsetX -= distanceX;
                offsetX = Math.min(offsetX, (bitmapShowWidth - getWidth()) / 2F);
                offsetX = Math.max(offsetX, -(bitmapShowWidth - getWidth()) / 2F);
            }
            if (bitmapShowHeight > getHeight()) {
                offsetY -= distanceY;
                offsetY = Math.min(offsetY, (bitmapShowHeight - getHeight()) / 2F);
                offsetY = Math.max(offsetY, -(bitmapShowHeight - getHeight()) / 2F);
            }
            invalidate();
            return false;
        }

        @Override
        public void onLongPress(MotionEvent e) {

        }

        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            float bitmapShowWidth = mBitmap.getWidth() * currentScale;
            float bitmapShowHeight = mBitmap.getHeight() * currentScale;
            if (bitmapShowWidth > getWidth() || bitmapShowHeight > getHeight()) {
                overScroller.fling((int) offsetX, (int) offsetY, (int) velocityX, (int) velocityY,
                        (int) (-((bitmapShowWidth - getWidth()) / 2)),
                        (int) (+((bitmapShowWidth - getWidth()) / 2)),
                        (int) (-((bitmapShowHeight - getHeight()) / 2)),
                        (int) (+((bitmapShowHeight - getHeight()) / 2)),
                        100,
                        100);
                postOnAnimation(this);
            }
            return false;
        }

        @Override
        public void run() {
            if (overScroller.computeScrollOffset()) {
                offsetX = overScroller.getCurrX();
                offsetY = overScroller.getCurrY();
                invalidate();
                postOnAnimation(this);
            }
        }
    }

    public float getCurrentScale() {
        return currentScale;
    }

    public void setCurrentScale(float fraction) {
        this.currentScale = fraction;
        invalidate();
    }

    class MyOnScaleGestureListener implements ScaleGestureDetector.OnScaleGestureListener {
        float initialScale;

        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            currentScale = initialScale * detector.getScaleFactor();
            invalidate();
            return false;
        }

        @Override
        public boolean onScaleBegin(ScaleGestureDetector detector) {
            initialScale = currentScale;
            return true;
        }

        @Override
        public void onScaleEnd(ScaleGestureDetector detector) {

        }
    }

}
